#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "automata.h"
#include "util.h"
#include "longInt.h"
#include "lambda.h"
#define NO_DEBUG

#ifdef DEBUG
    #define DEBUGM  printf
#else
    #define DEBUGM  //
#endif

void
removeLambdaTrans(t_table * t)
{
    int i, j, m, n, alphabetDim, cl, newState, aux, aux2, myself, mergeWith;
    t_longInt closure, checked, newSt, newSt2;
    t_longInt_vector closures;
    
    alphabetDim = strlen(t->alphabet);
  
    //Calculates Lambda Closures and adds final states discovered
    createVec(&closures,t->originalDim);
    for(i=0;i<t->originalDim;i++)
    {
        reset(checked);
        setLambdaClosure(i,t,closures.vec[i],checked);
        longIntMerge(closures.vec[i],t->states.vec[i]);
        closures. dim++;
        checkFinalInClosure(t,closures.vec[i],i);
    }
#ifdef DEBUG
    printf("------------------>\n");
    printf("Clausuras Lambda:\n");
    printStates2(&closures);
    printf("<------------------\n");
    system("pause");
#endif

    for(i=0;i<t->originalDim;i++)
    {  
        for(j=0;j<alphabetDim;j++)
        {
            myself = t->transitions.data[i][j];
            reset(newSt);
            reset(newSt2);
            if(myself != -1)
                longIntMerge(newSt,t->states.vec[myself]);
            for(m=0;m<CANT_INT;m++)
            {
                if(closures.vec[i][m]!=0)
                    for(n=0;n<32;n++)
                        if(1<<n & closures.vec[i][m])
                        {
                            mergeWith = t->transitions.data[n+32*m][j];
                            if(mergeWith != -1)
                                longIntMerge(newSt,t->states.vec[mergeWith]);
                        }
            }
            for(m=0;m<CANT_INT;m++)
                if(newSt[m]!=0)
                    for(n=0;n<32;n++)
                        if(1<<n & newSt[m])
                            longIntMerge(newSt2,closures.vec[n+32*m]);
            longIntMerge(newSt,newSt2);
            if(!isZero(newSt))
                t->transitions.data[i][j] = getStateIndex(t,newSt);
        }        
    }
    for(i=0;i<t->originalDim;i++)
        t->transitions.data[i][alphabetDim]=-1;
    recheckFinalStates(t);
}

void
checkFinalInClosure(t_table * t, t_longInt closure, int state)
{
    int i,j;
    t_longInt acum;
    t_longInt_vector lvec;
    
    reset(acum);
    for(i=0;i<t->originalFinalDim;i++)
        longIntMerge(acum,t->finalStates.vec[i]);
        
    longIntAnd(acum,closure);
    if(!isZero(acum) && !isFinalStInd(t,state))
    {
        createVec(&lvec,t->finalStates.dim+1);
        for(i=0;i<t->originalFinalDim;i++)
            longIntCpy(lvec.vec[i],t->finalStates.vec[i]);
        turnOn(lvec.vec[i++],state);
        for(j=i-1;j<t->finalStates.dim;j++)
            longIntCpy(lvec.vec[i++],t->finalStates.vec[j]);
        lvec.dim=t->finalStates.dim+1;
        free(t->finalStates.vec);
        t->originalFinalDim++;
        t->finalStates=lvec;
    }
}

void
recheckFinalStates(t_table * t)
{
    int i;
    t_longInt acum, aux;
    void * aux2;
    
    reset(acum);
    for(i=0;i<t->originalFinalDim;i++)
        longIntMerge(acum,t->finalStates.vec[i]);

    for(i=t->originalDim;i<t->states.dim;i++)
    {
        longIntCpy(aux,acum);
        longIntAnd(aux,t->states.vec[i]);
        if(!isZero(aux) && !isFinalStInd(t,i))
        {
            //Allocate more space if needed
            if(t->finalStates.dim == t->finalStates.maxDim)
            {
                if((aux2=realloc(t->finalStates.vec,
                    sizeof(t_longInt)* (t->finalStates.maxDim *= 2)))==NULL)
                    error(1,"Could not allocate memory. Shame on you.");
                else
                    t->finalStates.vec=aux2;
            }
            //Add new state into final states vector
            longIntCpy(t->finalStates.vec[t->finalStates.dim++],t->states.vec[i]);
        }
    }
        
}

void
setLambdaClosure(int state, t_table * t, t_longInt closure, t_longInt checked)
{
    int i, j, lambdaTo, alphabetDim;

    alphabetDim = strlen(t->alphabet);
    lambdaTo = t->transitions.data[state][alphabetDim];
    if(lambdaTo == -1 || lambdaTo == state || alreadySeen(lambdaTo,t->originalDim,checked))
        return ;

    turnOn(checked,state);
    
    if(lambdaTo >= t->originalDim)
    {
        for(i=0;i<CANT_INT;i++)
            for(j=0;j<32;j++)
                if(1<<j & (t->states.vec[lambdaTo])[i])
                {
                    if(alreadySeen(j+32*i,t->originalDim,checked))
                        continue;
                    t->transitions.data[state][alphabetDim] = j+32*i;
                    setLambdaClosure(state,t,closure,checked);
                }
        //Restore info
        t->transitions.data[state][alphabetDim] = lambdaTo;
        return ;
    }
        
    setLambdaClosure(lambdaTo,t,closure,checked);

    turnOn(closure,lambdaTo);
}


int
alreadySeen(int state, int originalDim, t_longInt l)
{
    t_longInt aux;
    
    if(state >= originalDim)
        return 0;
    
    reset(aux);
    turnOn(aux,state);
    longIntAnd(aux,l);
            
    return !isZero(aux);
}
